﻿package cz.dadul.adventure.controller
{
	/**
	 * 
	 * TODO: 
	 * Trida se stara o propojeni jednotlivych kontroleru
	 * + pridani MouseControlleru jako listenera pro IClickable items apod.
	 * 
	 * @author Dadul
	 */
	import flash.display.*;
	import flash.events.*;
	import flash.utils.*;
	import cz.dadul.adventure.model.*;
	import cz.dadul.adventure.view.*;
	import cz.dadul.adventure.model.events.*;
	 
	 public class MainController extends EventDispatcher
	{
		// Constants
		// Singleton instance
		private static var instance:MainController;
		
		// Controllers
		private var _layerController:LayerController;
		private var _playerController:PlayerController;
		private var _storageController:StorageController;
		private var _mouseController:MouseController;
		private var _soundController:SoundController;

		// Items
		private var _pickables:Array;
		private var _clickables:Vector.<*>;
		private var _items:Vector.<*>;

		public function MainController(p_key:SingletonBlocker):void{
			// this shouldn't be necessary unless they fake out the compiler:
			 if (p_key == null) {
				throw new Error("Error: Instantiation failed: Use MainController.getInstance() instead of new.");
			  }
			  init();
		}

			
		/**
		 * PRIVATE
		 */
		private function init():void { 
			_layerController = LayerController.getInstance();
			_playerController = PlayerController.getInstance();
			_storageController = StorageController.getInstance();
			_mouseController = MouseController.getInstance();
			_soundController = SoundController.getInstance();
			
			// Init of variables
			this._items = new Vector.<*>;
			this._clickables = new Vector.<*>;
			this._pickables = new Array();
		}

		
		
		/**
		 * PUBLIC		 
		 */
		/**
		 * Main function that starts the game
		 */
		public function start():void {
			setEvents();			
		}
		
		public function addItem(el:Item, layerName :String, x:int = 0, y:int = 0):Item {
			if ((el is PickableItem)||(el is ClickableItem)) {
				throw new Error("Clickable/Pickable items are dedicated only for the Main Layer!");
			}			
			this._items.push(el);
			// Adds to the view
			return _layerController.addItem(el, layerName, x, y);
		}
		 
		public function addMainItem(el:Item, x:int = 0, y:int = 0,  mainLayerType: int = Layer.STANDARD_LAYER):Item {
			if (el is PickableItem) {
				this._pickables[el.name] =  new PickableVectorItem(el as PickableItem, mainLayerType);
			}
			if ((el is PickableItem) || (el is ClickableItem)) {
				this._clickables.push(el);
			}
			this._items.push(el);
			
			// Adds to the view
			return _layerController.addMainItem(el, x, y, mainLayerType);
		}
		
		public function addLayer(layer:Layer, mainLayer:Boolean = false, x:int = 0, y:int = 0, parallaxFactor:Number = LayerController.AUTO_PARALLAX_FACTOR) : Layer {
			return _layerController.addLayer(layer, mainLayer, x, y, parallaxFactor);
		}
		
		public function addPlayer(p:Player, x:int = 0, y:int = 0):Player {
			// Adds to the player controller
			_playerController.player = p;	
			/**
			 * TODO: Nastavit cestu
			 */
			// Initialize path 
			_playerController.initPath();
			// Adds to the view
			return _layerController.addPlayer(p, x, y);
		}
		
		public function addStorage(storage:Storage):Storage {
			// Adds to the controller
			_storageController.storage = storage;
			// Adds to the view
			return _layerController.addStorage(storage);
		}
		
		public function initStage(stageWidth: int, stageHeight: int):void {
			_layerController.initStage(stageWidth, stageHeight);
		}
		
		
		/*
		 * PRIVATE
		 */ 
		private function setEvents():void {
			/*
			 * Set events for controllers
			 */
			_playerController.addEventListener(PlayerController.EVENT_PLAYER_MOVED, _layerController.updatelayers);			
			/*
			 *  Set events to all items
			 */ 
			for (var i:int = 0; i < this._items.length; i++) {
				this._items[i].addEventListener(Item.EVENT_REMOVE, this.onItemRemoved);
			}
			/*
			 * Set events for Clickable items
			 */
			for (var j:int = 0; j < this._clickables.length; j++) {
				this._clickables[j].addEventListener(ClickableItem.EVENT_CLICK, this.onItemClicked);
				/**
				 * TODO: Dodelat onItemOut, onItemOver - pro zmenu ukazatele mysi
				 */
			}
			
			/*
			 * Set events for Pickable items
			 */
			for (var k:String in this._pickables) {
				this._pickables[k].item.addEventListener(PickableItem.EVENT_PICK, this.onItemPicked);
				this._pickables[k].item.addEventListener(PickableItem.EVENT_PREPARE_TO_USE, this.onItemPrepareToUse);
			}
			 
			
			
		}			
		 
		private function onItemClicked(e: DataHolderEvent):void {
			trace("MC: clicked -> " + e.data);
			// Hide mouse cursour
			_mouseController.disablePointer();
		}
		
		private function onItemPicked(e: DataHolderEvent):void {
			trace("MC: picked -> " + e.data);
			// Remove the item from the main layer
			_layerController.removeMainItem(e.data);
			// Add the item to the StorageController
			_storageController.addItem(e.data);
			// Enable mouse controller
			_mouseController.enablePointer();
		}
		
		private function onItemPrepareToUse(e: DataHolderEvent):void {
			var prepareToUseItem:PickableItem = e.data;
			//--------------------------------------------
			trace("MC: ready to use -> " + prepareToUseItem);
			
			// Disable focus for all clickables
			clickablesEnableFocus(false);
			
			// Start to drag the item			
			_mouseController.startDrag(prepareToUseItem);			
			// Starts listening to the clicks -> it needs some delay
			var f:Function = function(prepareToUseItem:PickableItem):void { prepareToUseItem.addEventListener(MouseEvent.CLICK, onDraggedItemClick); }
			setTimeout(f, 20, prepareToUseItem);

		}
		
		private function onDraggedItemClick(e: MouseEvent):void {
			var prepareToUseItem:PickableItem = _mouseController.draggedItem;
			// Check for all the other items
			for (var i:uint =0; i < _items.length; i++) {
				if(prepareToUseItem != _items[i]){
					if (_items[i].hitTestObject(prepareToUseItem)) {						
						if (prepareToUseItem.useOnTarget(_items[i].name)) {
							// Remove the listener
							prepareToUseItem.removeEventListener(MouseEvent.CLICK, this.onDraggedItemClick);
							prepareToUseItem.addEventListener(PickableItem.EVENT_USE_AND_DOCK, this.onDraggedItemUseAndDock);	
							prepareToUseItem.addEventListener(PickableItem.EVENT_USE_AND_UNDOCK, this.onDraggedItemUseAndUndock);	
							prepareToUseItem.addEventListener(PickableItem.EVENT_USE_AND_REMOVE, this.onDraggedItemUseAndRemove);								
							// Successfull usage of the item
							_mouseController.stopDrag();
						}
						trace(_items[i].name);
						trace(prepareToUseItem.state);
						break;
					}
				}
			}
			
			// Check for the player
			if (_playerController.player.hitTestObject(prepareToUseItem)) {
				trace(_playerController.player.name);
			}
			// Check for the docking back to the storage
			if (_storageController.storage.hitTestObject(prepareToUseItem)) {
				// Remove the listener
				prepareToUseItem.removeEventListener(MouseEvent.CLICK, this.onDraggedItemClick);
				// Stop dragging
				_mouseController.stopDrag();	
				// Dock the item
				onDraggedItemUseAndDock(new DataHolderEvent("", prepareToUseItem));
			}
		}
		
		private function onDraggedItemUseAndDock(e: DataHolderEvent):void {
			// Change the state of the item
			e.data.docked = true;
			// Dock back
			_storageController.dock(e.data);
			// Post operations
			itemAfterUsage(e.data);	
		}

		private function onDraggedItemUseAndUndock(e: DataHolderEvent):void {
			// Add to the main layer
			e.data.docked = false;			
			_layerController.addMainItem(e.data, _pickables[e.data.name].x, _pickables[e.data.name].y, _pickables[e.data.name].mainLayerType);
			// Show the item
			e.data.show();
			// Post operations
			itemAfterUsage(e.data);		
		}

		private function onDraggedItemUseAndRemove(e: DataHolderEvent):void {
			_layerController.removeItem(e.data);
			// Remove the item from all fields
			_items.splice(_items.indexOf(e.data), 1);
			_clickables.splice(_clickables.indexOf(e.data), 1);			
			_pickables[e.data.name] = null;
			// Post operations
			itemAfterUsage(e.data);			
		}
		
		private function itemAfterUsage(usedItem: PickableItem):void {
			// Remove all listeners
			usedItem.removeEventListener(PickableItem.EVENT_USE_AND_DOCK, this.onDraggedItemUseAndDock);	
			usedItem.removeEventListener(PickableItem.EVENT_USE_AND_UNDOCK, this.onDraggedItemUseAndUndock);	
			usedItem.removeEventListener(PickableItem.EVENT_USE_AND_REMOVE, this.onDraggedItemUseAndRemove);					
			// Enable mouse pointer
			_mouseController.enablePointer();			
			// Enable focus for all clickables
			clickablesEnableFocus(true);			
		}
		
		private function clickablesEnableFocus(b:Boolean):void {
			for (var i:int = 0; i < this._clickables.length; i++) {
				this._clickables[i].enableFocus(b);
			}
		}
		
		private function onItemRemoved(e: DataHolderEvent):void {
			// Remove from the view
			_layerController.removeItem(e.data);
			// Remove from the arrays
			_items.splice(_items.indexOf(e.data), 1);
			if (e.data is ClickableItem) {
				_clickables.splice(_clickables.indexOf(e.data), 1);				
			}
			if (e.data is PickableItem) {
				_pickables[e.data.name] = null;
			}
		}
		
		/**
		 * STATIC
		 */
		public static function getInstance():MainController {
         if (instance == null) {
            instance = new MainController(new SingletonBlocker());
          }
         return instance;
		}
		
		/*
		 * 	SETTERS / GETTERS
		 */ 
		public function get view():Sprite {
			return _layerController;
		}
		 
	}
	
}

import cz.dadul.adventure.controller.*;
import cz.dadul.adventure.model.*;

class PickableVectorItem {
	private var _item:PickableItem;
	private var _mainLayerType:int;
	private var _x:int;
	private var _y:int;
	
	public function PickableVectorItem(item: PickableItem, mainLayerType: int) {
		_item = item;
		_x = item.x;
		_y = item.y;
		_mainLayerType = mainLayerType;
	}
	
	public function get item():PickableItem {
		return _item;
	}
	public function get x():int {
		return _x;
	}
	public function get y():int {
		return _y;
	}
	public function get mainLayerType():int {
		return _mainLayerType;
	}

}

internal class SingletonBlocker {}